#include "stdafx.h"
#include "pyhgeCore.h"
#include "pyhgeSprite.h"

PyTypeObject SpriteType=
{
	PyObject_HEAD_INIT(NULL)
	0,                         /*ob_size*/
	"hge.Sprite",             /*tp_name*/
	sizeof(pySprite), /*tp_basicsize*/
	0,                         /*tp_itemsize*/
	0,                         /*tp_dealloc*/
	0,                         /*tp_print*/
	0,                         /*tp_getattr*/
	0,                         /*tp_setattr*/
	0,                         /*tp_compare*/
	0,                         /*tp_repr*/
	0,                         /*tp_as_number*/
	0,                         /*tp_as_sequence*/
	0,                         /*tp_as_mapping*/
	0,                         /*tp_hash */
	0,                         /*tp_call*/
	0,                         /*tp_str*/
	0,                         /*tp_getattro*/
	0,                         /*tp_setattro*/
	0,                         /*tp_as_buffer*/
	Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,        /*tp_flags*/
	"Sprite(texture=None,x=0.0,y=0.0,w=0.0,h=0.0) -> Sprite Object\n"
	".....",/* tp_doc */
};

static void
Sprite_dealloc(PyObject * _self)
{
	pySprite * self = (pySprite*)_self;
	delete self->phgeSprite;
	Py_XDECREF(self->pTexture);
	self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
Sprite_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	pySprite * self = (pySprite*)type->tp_alloc(type, 0);
	if (!self) return NULL;
	self->phgeSprite = NULL;
	self->pTexture = Py_None;
	Py_INCREF(Py_None);
	return (PyObject*)self;
}

static int
Sprite_init(PyObject *_self, PyObject *args, PyObject *kwds)
{
	PyObject * pTex = Py_None;
	HTEXTURE tex;
	float x=0.0,y=0.0,w=0.0,h=0.0;
	static char *kwlist[] = {"texture", "x", "y", "w","h",0};
	pySprite * self = (pySprite*)_self;
	if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Offff:Sprite", kwlist,&pTex,&x,&y,&w,&h))
		return -1;
	if (pTex == Py_None)
	{
		tex = NULL;
	}
	else if (PyCObject_Check(pTex) && PyCObject_GetDesc(pTex)==DESC_HTEXTURE)
	{
		tex = (HTEXTURE)PyCObject_AsVoidPtr(pTex);
	}
	else
	{
		PyErr_SetString(PyExc_TypeError,"Sprite(): Texture object is required.");
		return -1;
	}
	Py_INCREF(pTex);
	Py_XDECREF(self->pTexture);
	self->pTexture = pTex;
	
	delete self->phgeSprite;
	self->phgeSprite = new hgeSprite(tex,x,y,w,h);
	
	return 0;
}

static PyObject *
Sprite_repr(PyObject * _self)
{
	pySprite * self = (pySprite*)_self;
	char buf[1024];
	float x,y,w,h;
	if (self->phgeSprite)
	{
		self->phgeSprite->GetTextureRect(&x,&y,&w,&h);
		sprintf(buf,"<Sprite texture=0x%X x=%.2f y=%.2f w=%.2f h=%.2f>",
			self->phgeSprite->GetTexture(),x,y,w,h);
	}
	else
	{
		sprintf(buf,"<Sprite NULL>");
	}
	return PyString_FromString(buf);
}
hgeSprite * Sprite_GetPtr(PyObject * obj)
{
	if (!PyObject_TypeCheck(obj,&SpriteType))
	{
		PyErr_SetString(PyExc_TypeError,"Sprite object is required.");
		return 0;
	}
	pySprite * ptr = (pySprite*)obj;
	if (ptr->phgeSprite == NULL)
	{
		PyErr_SetString(PyExc_ValueError,"NULL Sprite.");
		return 0;
	}
	return ptr->phgeSprite;
}
//start members
/*
Render
RenderEx
RenderStretch
Render4V
*/
DEFINE_PYTHON_FUNCTION(
"S.Render(x,y) -> None\n"
"Renders the sprite to the screen.",
METH_VARARGS,Sprite_Render)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y;
	if (!PyArg_ParseTuple(args,"ff:Render",&x,&y))
		return 0;
	spr->Render(x,y);
	Py_RETURN_NONE;
}

DEFINE_PYTHON_FUNCTION(
"S.RenderEx(x,y,rot,hscale=1.0,vscale=0.0) -> None\n"
"Renders the sprite to the screen with scaling and rotation.\n"
"If vscale==0.0 or omitted, hscale is used for both horizontal and vertical scaling. ",
METH_VARARGS,Sprite_RenderEx)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y,rot;
	float hscale = 1.0;
	float vscale = 0.0;
	if (!PyArg_ParseTuple(args,"fff|ff:RenderEx",&x,&y,&rot,&hscale,&vscale))
		return 0;
	spr->RenderEx(x,y,rot,hscale,vscale);
	Py_RETURN_NONE;
}

DEFINE_PYTHON_FUNCTION(
"S.RenderStretch(x1,y1,x2,y2) -> None \n"
"Renders the sprite to the screen with stretching. ",
METH_VARARGS,Sprite_RenderStretch)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x1,y1,x2,y2;
	if (!PyArg_ParseTuple(args,"ffff:RenderStretch",&x1,&y1,&x2,&y2))
		return 0;
	spr->RenderStretch(x1,y1,x2,y2);
	Py_RETURN_NONE;
}

DEFINE_PYTHON_FUNCTION(
"S.Render4V(x0,y0,x1,y1,x2,y2,x3,y3) -> None\n"
"Renders the sprite to arbitrary quad on the screen. ",
METH_VARARGS,Sprite_Render4V)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x0,y0,x1,y1,x2,y2,x3,y3;
	if (!PyArg_ParseTuple(args,"ffffffff:Render4V",&x0,&y0,&x1,&y1,&x2,&y2,&x3,&y3))
		return 0;
	spr->Render4V(x0,y0,x1,y1,x2,y2,x3,y3);
	Py_RETURN_NONE;
}
//texture
/*
SetTexture
GetTexture
*/
DEFINE_PYTHON_FUNCTION(
"S.SetTexture(texture) -> None\n"
"Changes the sprite texture. ",
METH_VARARGS,Sprite_SetTexture)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	PyObject * pObj;
	if (!PyArg_ParseTuple(args,"O:SetTexture",&pObj))
		return 0;
	if (pObj == Py_None || (PyCObject_Check(pObj) && PyCObject_GetDesc(pObj)==DESC_HTEXTURE))
	{
		Py_INCREF(pObj);
		Py_DECREF( ((pySprite*)self)->pTexture );
		((pySprite*)self)->pTexture = pObj;
		if (pObj == Py_None)
			spr->SetTexture(NULL);
		else
			spr->SetTexture((HTEXTURE)PyCObject_AsVoidPtr(pObj));
		Py_RETURN_NONE;
	}
	else
	{
		PyErr_SetString(PyExc_TypeError,"Texture object is required.");
		return 0;
	}
}
DEFINE_PYTHON_FUNCTION(
"S.GetTexture() -> None\n"
"Returns the current sprite texture.",
METH_NOARGS,Sprite_GetTexture)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	Py_INCREF(((pySprite*)self)->pTexture);
	return ((pySprite*)self)->pTexture;
}
/*
SetTextureRect
GetTextureRect
*/
DEFINE_PYTHON_FUNCTION(
"S.SetTextureRect(x,y,w,h,adjSize=True)\n"
"Sets the texture region to use for the sprite.\n"
"If adjSize is true, the sprite dimensions will be adjusted according to new texture region.\n"
"If adjSize is false the sprite dimensions will remain as is.",
METH_VARARGS,Sprite_SetTextureRect)
{
	float x,y,w,h;
	PyObject *adjSize = Py_True;
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	if (!PyArg_ParseTuple(args,"ffff|O:SetTextureRect",&x,&y,&w,&h,&adjSize))
		return 0;
	int bval = PyObject_IsTrue(adjSize);
	if (bval == -1) return 0;
	spr->SetTextureRect(x,y,w,h,bval>0);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
"S.SetTextureRect() -> (x,y,w,h)\n"
"Returns the current texture region used for the sprite.",
METH_NOARGS,Sprite_GetTextureRect)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y,w,h;
	spr->GetTextureRect(&x,&y,&w,&h);
	return Py_BuildValue("ffff",x,y,w,h);
}
/*
SetColor
GetColor
SetZ
GetZ
*/
DEFINE_PYTHON_FUNCTION(
"S.SetColor(color,i=-1) -> None\n"
"Sets tint and alpha for the specified vertex or the entire sprite. \n"
"i is index of the vertex to be changed. Vertices are indexed clockwise starting from top-left one in the range 0-3.",
METH_VARARGS,Sprite_SetColor)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	DWORD color;
	int i = -1;
	if (!PyArg_ParseTuple(args,"I|i:SetColor",&color,&i))
		return 0;
	spr->SetColor(color,i);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
"S.GetColor(i=0) -> int\n"
"Returns color of the specified sprite vertex.\n"
"i is index of the sprite vertex.",
METH_VARARGS,Sprite_GetColor)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	int i=0;
	if (!PyArg_ParseTuple(args,"|i:GetColor",&i))
		return 0;
	return PyInt_FromSize_t(spr->GetColor(i));
}

DEFINE_PYTHON_FUNCTION(
"S.SetZ(z,i=-1) -> None\n"
"Sets Z-order for the specified vertex or the entire sprite. \n"
"i is index of the vertex to be changed. Vertices are indexed clockwise starting from top-left one in the range 0-3.",
METH_VARARGS,Sprite_SetZ)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float z;
	int i=-1;
	if (!PyArg_ParseTuple(args,"f|i:SetZ",&z,&i))
		return 0;
	spr->SetZ(z,i);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
"S.GetZ(i=0) -> float\n"
"Returns Z-order of the specified sprite vertex.\n"
"i is index of the sprite vertex.",
METH_VARARGS,Sprite_GetZ)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	int i=0;
	if (!PyArg_ParseTuple(args,"|i:GetZ",&i))
		return 0;
	return PyFloat_FromDouble((double)spr->GetZ(i));
}
/*
SetBlendMode
SetHotSpot
SetFlip
*/
DEFINE_PYTHON_FUNCTION(
"S.SetBlendMode(blend) -> None\n"
"Sets the sprite blending mode. ",
METH_VARARGS,Sprite_SetBlendMode)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	int blend;
	if (!PyArg_ParseTuple(args,"i:SetBlendMode",&blend))
		return 0;
	spr->SetBlendMode(blend);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
"S.SetHotSpot(x,y) -> None\n"
"Sets the sprite anchor point. ",
METH_VARARGS,Sprite_SetHotSpot)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y;
	if (!PyArg_ParseTuple(args,"ff:SetHotSpot",&x,&y))
		return 0;
	spr->SetHotSpot(x,y);
	Py_RETURN_NONE;
}
DEFINE_PYTHON_FUNCTION(
"S.SetFlip(bX,bY,bHotSpot=False) -> None\n"
"Flips the sprite horizontally and/or vertically.",
METH_VARARGS,Sprite_SetFlip)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	PyObject *bx,*by;
	PyObject *bHotSpot = Py_False;
	if (!PyArg_ParseTuple(args,"OO|O:SetFlip",&bx,&by,&bHotSpot))
		return 0;
	int bval1,bval2,bval3;
	bval1 = PyObject_IsTrue(bx);if (bval1==-1) return 0;
	bval2 = PyObject_IsTrue(by);if (bval2==-1) return 0;
	bval3 = PyObject_IsTrue(bHotSpot);if (bval3==-1) return 0;
	spr->SetFlip(bval1>0,bval2>0,bval3>0);
	Py_RETURN_NONE;
}
/*
GetBlendMode
GetHotSpot
GetFlip
*/
DEFINE_PYTHON_FUNCTION(
"S.GetBlendMode() -> int\n"
"Returns the sprite blend mode.",
METH_NOARGS,Sprite_GetBlendMode)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	return PyInt_FromLong(spr->GetBlendMode());
}
DEFINE_PYTHON_FUNCTION(
"S.GetHotSpot() -> (x,y)\n"
"Returns the sprite anchor point. ",
METH_NOARGS,Sprite_GetHotSpot)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y;
	spr->GetHotSpot(&x,&y);
	return Py_BuildValue("ff",x,y);
}
DEFINE_PYTHON_FUNCTION(
"S.GetFlip() -> (bx,by)\n"
"Returns the current sprite flipping.",
METH_NOARGS,Sprite_GetFlip)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	bool bx,by;
	spr->GetFlip(&bx,&by);
	return Py_BuildValue("OO",bx?Py_True:Py_False,by?Py_True:Py_False);
}
/*
GetWidth
GetHeight
GetBoundingBox
GetBoundingBoxEx
*/
DEFINE_PYTHON_FUNCTION(
"S.GetWidth() -> float\n"
"Returns the sprite width.",
METH_NOARGS,Sprite_GetWidth)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	return PyFloat_FromDouble((double)spr->GetWidth());
}
DEFINE_PYTHON_FUNCTION(
"S.GetHeight() -> float\n"
"Returns the sprite height.",
METH_NOARGS,Sprite_GetHeight)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	return PyFloat_FromDouble((double)spr->GetHeight());
}
DEFINE_PYTHON_FUNCTION(
"S.GetBoundindBox(x,y) -> (x1,y1,x2,y2)\n"
"Returns the sprite bounding box. ",
METH_VARARGS,Sprite_GetBoundingBox)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y;
	if (!PyArg_ParseTuple(args,"ff",&x,&y))
		return 0;
	hgeRect rect;
	spr->GetBoundingBox(x,y,&rect);
	return Py_BuildValue("ffff",rect.x1,rect.y1,rect.x2,rect.y2);
}
DEFINE_PYTHON_FUNCTION(
"S.GetBoundingBoxEx(x,y,rot,hscale,vscale) -> (x1,y1,x2,y2)\n"
"Returns the scaled and rotated sprite bounding box.",
METH_VARARGS,Sprite_GetBoundingBoxEx)
{
	hgeSprite * spr = Sprite_GetPtr(self);
	if (!spr) return 0;
	float x,y,rot,hscale,vscale;
	if (!PyArg_ParseTuple(args,"fffff",&x,&y,&rot,&hscale,&vscale))
		return 0;
	hgeRect rect;
	spr->GetBoundingBoxEx(x,y,rot,hscale,vscale,&rect);
	return Py_BuildValue("ffff",rect.x1,rect.y1,rect.x2,rect.y2);
}
DEFINE_PYTHON_FUNCTION(
"S.Clone() -> Sprite Object",
METH_NOARGS,Sprite_Clone)
{
	if (!Sprite_GetPtr(self)) return 0;
	pySprite * _self = (pySprite*)self;
	pySprite * result = (pySprite *)SpriteType.tp_alloc(&SpriteType,0);
	if (!result) return 0;
	result->phgeSprite = new hgeSprite(*(_self->phgeSprite));
	result->pTexture = _self->pTexture;
	Py_XINCREF(result->pTexture);
	return (PyObject*)result;
}
static PyMethodDef Sprite_methods[] = {
	{"Render",Sprite_Render,Sprite_Render_flag,Sprite_Render_doc},
	{"RenderEx",Sprite_RenderEx,Sprite_RenderEx_flag,Sprite_RenderEx_doc},
	{"RenderStretch",Sprite_RenderStretch,Sprite_RenderStretch_flag,Sprite_RenderStretch_doc},
	{"Render4V",Sprite_Render4V,Sprite_Render4V_flag,Sprite_Render4V_doc},
	{"SetTexture",Sprite_SetTexture,Sprite_SetTexture_flag,Sprite_SetTexture_doc},
	{"GetTexture",Sprite_GetTexture,Sprite_GetTexture_flag,Sprite_GetTexture_doc},
	{"SetTextureRect",Sprite_SetTextureRect,Sprite_SetTextureRect_flag,Sprite_SetTextureRect_doc},
	{"GetTextureRect",Sprite_GetTextureRect,Sprite_GetTextureRect_flag,Sprite_GetTextureRect_doc},
	{"SetColor",Sprite_SetColor,Sprite_SetColor_flag,Sprite_SetColor_doc},
	{"GetColor",Sprite_GetColor,Sprite_GetColor_flag,Sprite_GetColor_doc},
	{"SetZ",Sprite_SetZ,Sprite_SetZ_flag,Sprite_SetZ_doc},
	{"GetZ",Sprite_GetZ,Sprite_GetZ_flag,Sprite_GetZ_doc},
	{"SetBlendMode",Sprite_SetBlendMode,Sprite_SetBlendMode_flag,Sprite_SetBlendMode_doc},
	{"SetHotSpot",Sprite_SetHotSpot,Sprite_SetHotSpot_flag,Sprite_SetHotSpot_doc},
	{"SetFlip",Sprite_SetFlip,Sprite_SetFlip_flag,Sprite_SetFlip_doc},
	{"GetBlendMode",Sprite_GetBlendMode,Sprite_GetBlendMode_flag,Sprite_GetBlendMode_doc},
	{"GetHotSpot",Sprite_GetHotSpot,Sprite_GetHotSpot_flag,Sprite_GetHotSpot_doc},
	{"GetFlip",Sprite_GetFlip,Sprite_GetFlip_flag,Sprite_GetFlip_doc},
	{"GetWidth",Sprite_GetWidth,Sprite_GetWidth_flag,Sprite_GetWidth_doc},
	{"GetHeight",Sprite_GetHeight,Sprite_GetHeight_flag,Sprite_GetHeight_doc},
	{"GetBoundingBox",Sprite_GetBoundingBox,Sprite_GetBoundingBox_flag,Sprite_GetBoundingBox_doc},
	{"GetBoundingBoxEx",Sprite_GetBoundingBoxEx,Sprite_GetBoundingBoxEx_flag,Sprite_GetBoundingBoxEx_doc},
	//this is special for python
	{"Clone",Sprite_Clone,Sprite_Clone_flag,Sprite_Clone_doc},
	{NULL}  /* Sentinel */
};


int
pyInitSprite(PyObject *mod)
{
	SpriteType.tp_new = Sprite_new;
	SpriteType.tp_dealloc = Sprite_dealloc;
	SpriteType.tp_init = Sprite_init;
	SpriteType.tp_repr = Sprite_repr;
	SpriteType.tp_str = Sprite_repr;
	SpriteType.tp_methods = Sprite_methods;
	if (PyType_Ready(&SpriteType) <0) return -1;
	Py_INCREF(&SpriteType);
	return PyModule_AddObject(mod, "Sprite", (PyObject *)&SpriteType); 
}